// 高级类型   ===============================

// 可以为null的类型 + 字符串字面量类型





// 联合类型 string | number
function padLeft(value:string, padding:string | number){
    if(typeof padding === 'number'){
        return Array(padding+1).join(' ')+value
    }
    if(typeof padding === 'string'){
        return padding + value
    }
    throw new Error('Expected string or number got '+padding)
}
console.log(padLeft('hello hardy', true));
// ---交叉类型和类型保护
interface Bird {
    fly();
    layEggs();
}
interface Fish {
    swim();
    layEggs();
}
function getSmallPet():Fish | Bird {}
let pet = getSmallPet();
pet.layEggs();  // 能调到
pet.fly();     // 调不到
// 因为声明类型是Fish | Bird，所以只能调用到公有的部分
// 一般在js中会这么写 , 可以加上类型断言防止取属性报错
if((pet as Fish).swim){
    (pet as Fish).swim()
}else if((pet as Bird).fly){
    (pet as Bird).fly()
}
// 在ts中可以这么写
function isFish(pet: Fish | Bird): pet is Fish{
    return (pet as Fish).swim !== undefined;
}
// 然后上面的代码可以改成
if(isFish(pet)){
    pet.swim()
}else{
    pet.fly()
}
// ---重写上面的例子
function isNumber(x:any): x is number{
    return typeof x === 'number'
}
function isString(x:any):x is string{
    return typeof x === 'string'
}
function padLeft1(value:string, padding:string | number){
    if(isNumber(padding)){
        return
    }
    if(isString(padding)){
        throw new Error(`Expected string or number got ${padding}`)
}















// 交叉类型
function extend<T, U>(first:T, second:U): T & U {
    let result = {} as T & U;  // 类型断言
    for(let id in first) {
        result[id] = first[id] as any
    }
    for(let id in second) {
        if (!result.hasOwnProperty(id)){
            result[id] = second[id] as any
        }
    }
    return result
}
// ------------------------------
class Person {
    constructor(public name:string){}
}
interface logabal {
    log():void
}
class Consolelogabal implements logabal{
    log(){}
}
let jim = extend(new Person('hardy'),new Consolelogabal())
jim.name = '小方块';
jim.log();



// 类型推断
class Animal {}
class Tigger extends Animal{}
class Lion extends Animal{}
function create1():Animal[]{
    return [new Tigger(), new Lion()];
}

















// 泛型约束
// 工厂函数中的应用
function create<T>(c:{ new():T }): T{
    return new c()
}



// 泛型K受T约束
function getProperty<T, K extends keyof T>(obj:T, key:K) {
    return obj[key]
}
let x = { a: 1, b:2, c:3 };
getProperty(x, 'a');


// 通过接口约束T类型
interface Lengthwise {
    length: number
}
function loggingIdentify<T extends Lengthwise>(arg:T): T {
    console.log(arg.length);
    return arg
}


//泛型类
class GenericNumber<T> {
    zeroValue: T
    add: (x:T, y:T) => T
}
// 给泛型定义number
let genericNumber = new GenericNumber<number>()
genericNumber.zeroValue = 1;
genericNumber.add = (x, y) => {
    return x + y;
};

//给泛型定义字符串
let genericNumber2 = new GenericNumber<string>()
genericNumber2.zeroValue = 'hardy';
genericNumber2.add = (x, y) => {
    return x + y + '小方块'
}

// -------------------
function identify<T>(args:T):T {
    return args
}
interface GenericIdentityFn<T> {
    (arg:T): T
}
let myIdentify: GenericIdentityFn<number> = identify;













// this + 重载
interface UIElement {
    addClickListener(onclick:(this:void, e:Event) => void): void
}
class Handler {
    type: string;
    onClickBad(e:Event){
        this.type = e.type
    }
}
let h = new Handler();
let uiElement:UIElement = {
    addClickListener(){}
};
uiElement.addClickListener(h.onClickBad);

// =====================================================================
interface Card {
    suit:string,
    card:number
}

interface Desk {
    suits: string[],
    cards: number[],
    createPickerCard(this:Desk): () => Card
}

let desk:Desk = {
    suits: ['hearts', 'spades', 'clubs', 'diamonds'],
    cards: Array(52),
    createPickerCard: function(this:Desk) {
        return () => {
            let pickerCard = Math.floor(Math.random()*52);
            let cardPicker = Math.floor(pickerCard / 13);
            return {
                suit: this.suits[cardPicker],
                card: pickerCard % 13
            }
        }
    }
}

let cardPicker = desk.createPickerCard();
let pickerCard = cardPicker();
console.log('card:', pickerCard.card + '===' + pickerCard.suit);





// 可选参数 和默认参数
// 可选参数参数后+?
function buildName(firstName:string,lastName?:string, ...args):string {
    return `${firstName}-${lastName}`
}
let name1 = buildName('hardy1');
let name2 = buildName('hardy1', 'hardy2');
let name3 = buildName('hardy1', 'hardy2', 'hardy3');

let buildName1:(fname:string, ...rest:string[])=>string = buildName;




// 命名函数
function add(x:number, y:number): number {
    return x + y;
}

//匿名函数
let myFunction:(baseValue:number, increment:number)=> number = function(x:number, y:number):number{
    return x + y;
}